package puzzle.common.view.swing;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Stroke;
import java.awt.geom.AffineTransform;
import java.util.ConcurrentModificationException;

import javax.swing.JPanel;

import puzzle.common.data.AbstractBoard;
import tomoto.draw.DrawUtil;

public abstract class AbstractPuzzleViewPanel<B extends AbstractBoard<B>> extends JPanel {
	private static final long serialVersionUID = 1L;

	protected B m_board;
	protected int m_cellSize;
	protected int m_dotSize = 3;
	protected int m_inset = 8;
	protected Stroke m_lineStroke = new BasicStroke(3.0f);
	protected Stroke m_thinStroke = new BasicStroke(1.0f);

	public AbstractPuzzleViewPanel() {
		setPreferredSize(new Dimension(300, 200));
	}

	public synchronized B getBoard() {
		return m_board;
	}

	@Override
	public void setFont(Font font) {
		super.setFont(font);
		m_cellSize = font.getSize() + 6;
	}

	public synchronized void setBoard(B board) {
		m_board = board;
		adjustPreferredSize();
	}

	private void adjustPreferredSize() {
		setPreferredSize(new Dimension(m_board.getLogicalWidth() * m_cellSize + m_inset * 2,
				m_board.getLogicalHeight() * m_cellSize + m_inset * 2));
	}

	public void adjustFontSize() {
		int boardSize = Math.max(m_board.getLogicalWidth(), m_board.getLogicalHeight());
		float size = boardSize < 15 ? 20 : boardSize < 30 ? 16 : 12;
		setFont(getFont().deriveFont(size));
		adjustPreferredSize();
	}

	protected void drawCross(Graphics2D g2, int x, int y, int r) {
		g2.setStroke(m_thinStroke);
		g2.drawLine(x - r, y - r, x + r, y + r);
		g2.drawLine(x + r, y - r, x - r, y + r);
	}
	
	protected void drawStringInCell(Graphics2D g2, int x, int y, String s) {
		int x1 = x * m_cellSize + m_cellSize / 2;
		int y1 = y * m_cellSize + m_cellSize / 2;
		DrawUtil.drawCenteredString(g2, x1, y1, s);
	}
	
	protected void drawGrid(Graphics2D g2, AbstractBoard<?> b) {
		g2.setColor(Color.BLACK);
		g2.setStroke(m_thinStroke);
		DrawUtil.drawGrid(g2, 0, 0, m_cellSize, m_cellSize, b.width, b.height);
	}

	@Override
	public void paintComponent(Graphics g) {
		super.paintComponent(g);

		Graphics2D g2 = (Graphics2D) g;
		g2.transform(new AffineTransform(1, 0, 0, 1, m_inset, m_inset));
		
		while (true) {
			B b = getBoard();
	
			if (b == null) {
				return;
			}
	
			try {
				paintBoard(g2, b);
			} catch (ConcurrentModificationException ex) {
				continue; // retry
			}
			break; // done
		}
	}

	protected abstract void paintBoard(Graphics2D g2, B b);

}
